import java.io.*;
import java.net.*;
import java.util.*;

public class IRCConnection extends Thread
{
	// State variables
	private boolean running = true;

	// Connection variables
	private String server;
	private int port;
	private String user;
	private String pass;
	private HashMap<String, String> channels;

	private Socket socket = null;
	private BufferedReader in = null;
	private BufferedWriter out = null;

	// Buffer variables
	private int buffer_pos = 0;
	private LinkedList<String> buffer = new LinkedList<String>();


	public IRCConnection(String server, int port, String user, String pass, HashMap<String, String> channels)
	{
		this.server = server;
		this.port = port;

		this.user = user;
		this.pass = pass;

		this.channels = channels;
	}

	public void run()
	{
		while (running)
		{
			try
			{
				// Need to open ourself?
				if (socket == null)
					open();

				String line;

				// Read loop
				while (running && (line = in.readLine()) != null)
				{
					//System.out.println(line);

					// Respond to pings
					if (line.startsWith("PING"))
						write("PONG " + line.substring(5));

					// Join channels
					if (line.contains(user) && line.contains(":+ix"))
					{
						for (String channel : channels.keySet())
							write("JOIN " + channel + " " + channels.get(channel));
					}

					// (Re)Identify if needed
					if (line.equals(":NickServ!services@services.synirc.net NOTICE " + user + " :This nickname is registered and protected.  If it is your"))
						sendMsg("NickServ", "IDENTIFY " + pass);

					// Get nick back if needed
					if (line.contains("433") && line.contains(":Nickname is already in use."))
					{
						write("NICK " + user + "temp");
						sendMsg("NickServ", "GHOST " + user + " " + pass);
						write("NICK " + user);
					}

					buffer.add(line);
				}
			}
			catch (Exception e)
			{
				// Not exactly sure how to handle exceptions with the refactoring
				e.printStackTrace();
			}

			close();

			buffer_pos = 0;
			buffer.clear();

			// Wait 30s (to avoid too many connections issue
			try { Thread.sleep(30000); } catch (Exception e) { }
		}
	}

	// Stop running
	public void die()
	{
		running = false;
	}

	// Sets up the connection
	private void open()
		throws IOException
	{
		socket = new Socket(server, port);
		in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

		// Critical failure if this happened
		// Should have thrown an exception for a normal error
		if (!socket.isConnected())
		{
			System.out.println("Failed to connect!");
			System.exit(0);
		}

		// Set up username
		write("USER " + user + " \"\" \"\" :" + user);
		write("NICK " + user);
	}

	// Closes the connection
	private void close()
	{
		try { in.close(); } catch (Exception e) { }
		try { out.close(); } catch (Exception e) { }
		try { socket.close(); } catch (Exception e) { }

		in = null;
		out = null;
		socket = null;
	}

	// Reads a line from the buffer
	public String readLine()
	{
		// Reset the buffer position
		buffer_pos = 0;

		if (buffer.isEmpty())
		{
			return null;
		}
		else
		{
			String ret = buffer.get(0);
			buffer.removeFirst();
			return ret;
		}
	}

	// Reads a line from the buffer, but does not remove it
	public String readLineBuffered()
	{
		if (buffer_pos >= buffer.size())
		{
			return null;
		}
		else
		{
			String ret = buffer.get(buffer_pos);
			buffer_pos++;
			return ret;
		}
	}

	// Write raw data out
	public void write(String data)
		throws IOException
	{
		out.write(data + "\n");
		out.flush();
	}

	// Send out a (potentially multi-line) PRIVMSG
	public void sendMsg(String target, String msg)
	{
		sendMsg(target, msg, false);
	}

	// Send out a (potentially multi-line) NOTICE
	public void sendNotice(String target, String msg)
	{
		sendMsg(target, msg, true);
	}

	// Breaks up multi-line messages and sends them
	private void sendMsg(String target, String msg, boolean isNotice)
	{
		// Can't send if the connection isn't set up, so queue instead
		if (out == null)
		{
			// Dependent on LeagueBot = unfortunate
			// Forces PRIVMSG
			QueuedMessage qm = new QueuedMessage(target, msg, 1000);
			qm.start();
			return;
		}

		if (target == null || target.length() == 0 || msg == null || msg.length() == 0)
			return;

		try
		{
			String[] msgLines = msg.split("\n");

			// Send a message for every line
			for (String line : msgLines)
			{
				if (line.length() == 0)
					continue;

				// I don't like this, but it should work for now
				LeagueBot.logAdmin("MSG (" + target + ") - " + line);

				// Split messages that are too long into multiple lines
				int i = 0;
				line += " ";
				do
				{
					int idx = line.lastIndexOf(" ", i + 435);

					if (isNotice)
						out.write("NOTICE ");
					else
						out.write("PRIVMSG ");

					out.write(target);
					out.write(" :");
					out.write(line.substring(i, idx));
					out.write("\n");
					out.flush();

					i = idx + 1;
				} while (i < line.length());
			}
		}
		catch (Exception e) { e.printStackTrace(); }
	}
}
